﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;

namespace NGS.BioSequenceIO
{
    public interface INgsReader
    {
        byte[] ReadSeqLine();
        void Finish();
    }
    public class TxtNgsReader : INgsReader
    {
        Stream stream;
        public bool bEOF;
        public int bufSize = 4096;
        public byte[] buff;
        public int nReadLen, nStartPos, nLineLen;
        public TxtNgsReader(string filename)
        {
            FileStream fs = File.OpenRead(filename);
            if (filename.IndexOf(".gz") > 0)
            {
                GZipStream gstream = new GZipStream(fs, CompressionMode.Decompress);
                stream = (Stream)gstream;
            }
            else
            {
                stream = (Stream)fs;
            }
            buff = new byte[bufSize];
            nReadLen = stream.Read(buff, 0, bufSize);
            if (nReadLen < bufSize)
                bEOF = true;
            else
                bEOF = false;

            nStartPos = 0;//下一個讀取位置
        }
        private void RechargeBuf()
        {
            int nRemainder = nReadLen - nStartPos;
            if (nRemainder > 0)
            {
                byte[] buff2 = new byte[bufSize];
                Array.Copy(buff, nStartPos, buff2, 0, nRemainder);
                nReadLen = nRemainder + stream.Read(buff2, nRemainder, bufSize - nRemainder);
                buff = buff2;
            }
            else
            {
                nReadLen = stream.Read(buff, 0, bufSize);
            }
            if (nReadLen < bufSize)
                bEOF = true;

            nStartPos = 0;
        }
        public void Finish()
        {
            stream.Close();
        }
        public bool Peek()//這裡就先準備好一行的資料
        {
            if (nStartPos == nReadLen && bEOF) return false;
            if (nStartPos == nReadLen) RechargeBuf();
            while (buff[nStartPos] < 20 && nStartPos < nReadLen)
            {
                nStartPos++;
                if (nStartPos == nReadLen)
                {
                    if (bEOF)
                    {
                        break;//沒東西了
                    }
                    RechargeBuf();
                    if (nReadLen == 0)
                        return false;//沒東西了
                }
            }

            int nEndPos = nStartPos;
            while (buff[nEndPos] >= 20 && nEndPos < nReadLen)
            {
                nEndPos++;
                if (nEndPos >= nReadLen)
                {
                    if (bEOF)
                    {
                        break;//沒東西了
                    }
                    nEndPos -= nStartPos;
                    RechargeBuf();
                    if (nReadLen <= nEndPos)
                    {
                        break;//沒東西了
                    }
                }
            }
            nLineLen = nEndPos - nStartPos;
            if (nLineLen > 0)
                return true;
            return false;
        }
        protected void AdvanceLine()
        {
            if (!Peek()) return;
            nStartPos += nLineLen;
        }
        public virtual byte[] ReadSeqLine()
        {
            if (!Peek()) return null;
            byte[] line = new byte[nLineLen];
            Array.Copy(buff, nStartPos, line, 0, nLineLen);
            nStartPos += nLineLen;
            return line;
        }
    }
    public class FastaNgsReader : TxtNgsReader
    {
        public FastaNgsReader(string filename) : base(filename) { }
        public override byte[] ReadSeqLine()
        {
            if (!Peek()) return null;
            while (buff[nStartPos] == 62 || buff[nStartPos] == 59)//62=0x3E='>' 59=0x3B=';'
            {
                nStartPos += nLineLen;
                if (!Peek()) return null;
            }
            byte[] line = new byte[nLineLen];
            Array.Copy(buff, nStartPos, line, 0, nLineLen);
            nStartPos += nLineLen;
            return line;
        }
    }
    public class FastqNgsReader : TxtNgsReader
    {
        public FastqNgsReader(string filename) : base(filename) { }
        public override byte[] ReadSeqLine()
        {
            if (!Peek()) return null;
            while (buff[nStartPos] != 64)//64=0x40='@'
            {
                nStartPos += nLineLen;
                if (!Peek()) return null;
            }
            nStartPos += nLineLen;
            if (!Peek()) return null;
            byte[] line = new byte[nLineLen];

            Array.Copy(buff, nStartPos, line, 0, nLineLen);
            nStartPos += nLineLen;

            //下二行都不要
            AdvanceLine();
            AdvanceLine();
            return line;
        }
    }
}
